home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / nntpcunx.c < prev    next >
C/C++ Source or Header  |  1996-05-15  |  35KB  |  1,186 lines

  1. /*
  2.  * Program:    Network News Transfer Protocol (NNTP) client routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 January 1993
  13.  * Last Edited:    15 May 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include "mail.h"
  38. #include "osdep.h"
  39. #include <ctype.h>
  40. #include <stdio.h>
  41. #include <errno.h>
  42. extern int errno;        /* just in case */
  43. #include <sys/stat.h>
  44. #include "smtp.h"
  45. #include "nntp.h"
  46. #include "nntpcunx.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "newsrc.h"
  50.  
  51. /* NNTP mail routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER nntpdriver = {
  57.   "nntp",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   nntp_valid,            /* mailbox is valid for us */
  60.   nntp_parameters,        /* manipulate parameters */
  61.   nntp_find,            /* find mailboxes */
  62.   nntp_find_bboards,        /* find bboards */
  63.   nntp_find_all,        /* find all mailboxes */
  64.   nntp_find_all_bboards,    /* find all bboards */
  65.   nntp_subscribe,        /* subscribe to mailbox */
  66.   nntp_unsubscribe,        /* unsubscribe from mailbox */
  67.   nntp_subscribe_bboard,    /* subscribe to bboard */
  68.   nntp_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   nntp_create,            /* create mailbox */
  70.   nntp_delete,            /* delete mailbox */
  71.   nntp_rename,            /* rename mailbox */
  72.   nntp_mopen,            /* open mailbox */
  73.   nntp_close,            /* close mailbox */
  74.   nntp_fetchfast,        /* fetch message "fast" attributes */
  75.   nntp_fetchflags,        /* fetch message flags */
  76.   nntp_fetchstructure,        /* fetch message envelopes */
  77.   nntp_fetchheader,        /* fetch message header only */
  78.   nntp_fetchtext,        /* fetch message body only */
  79.   nntp_fetchbody,        /* fetch message body section */
  80.   nntp_setflag,            /* set message flag */
  81.   nntp_clearflag,        /* clear message flag */
  82.   nntp_search,            /* search for message based on criteria */
  83.   nntp_ping,            /* ping mailbox to see if still alive */
  84.   nntp_check,            /* check for new messages */
  85.   nntp_expunge,            /* expunge deleted messages */
  86.   nntp_copy,            /* copy messages to another mailbox */
  87.   nntp_move,            /* move messages to another mailbox */
  88.   nntp_append,            /* append string message to mailbox */
  89.   nntp_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM nntpproto = {&nntpdriver};
  94.  
  95. /* NNTP mail validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *nntp_valid (char *name)
  101. {
  102.                 /* must be bboard */
  103.   return *name == '*' ? mail_valid_net (name,&nntpdriver,NIL,NIL) : NIL;
  104. }
  105.  
  106.  
  107. /* News manipulate driver parameters
  108.  * Accepts: function code
  109.  *        function-dependent value
  110.  * Returns: function-dependent return value
  111.  */
  112.  
  113. void *nntp_parameters (long function,void *value)
  114. {
  115.   return NIL;
  116. }
  117.  
  118. /* NNTP mail find list of mailboxes
  119.  * Accepts: mail stream
  120.  *        pattern to search
  121.  */
  122.  
  123. void nntp_find (MAILSTREAM *stream,char *pat)
  124. {
  125.   /* Always a no-op */
  126. }
  127.  
  128.  
  129. /* NNTP mail find list of bboards
  130.  * Accepts: mail stream
  131.  *        pattern to search
  132.  */
  133.  
  134. void nntp_find_bboards (MAILSTREAM *stream,char *pat)
  135. {
  136.                 /* use .newsrc if a stream given */
  137.   if (stream && !stream->anonymous) newsrc_find (pat);
  138. }
  139.  
  140. /* NNTP mail find list of all mailboxes
  141.  * Accepts: mail stream
  142.  *        pattern to search
  143.  */
  144.  
  145. void nntp_find_all (MAILSTREAM *stream,char *pat)
  146. {
  147.   /* Always a no-op */
  148. }
  149.  
  150.  
  151. /* NNTP mail find list of all bboards
  152.  * Accepts: mail stream
  153.  *        pattern to search
  154.  */
  155.  
  156. void nntp_find_all_bboards (MAILSTREAM *stream,char *pat)
  157. {
  158.   char *s,*t,*bbd,*patx,tmp[MAILTMPLEN];
  159.                 /* use .newsrc if a stream given */
  160.   if (stream && LOCAL && LOCAL->nntpstream) {
  161.                 /* begin with a host specification? */
  162.     if (((*pat == '{') || ((*pat == '*') && (pat[1] == '{'))) &&
  163.     (t = strchr (pat,'}')) && *(patx = ++t)) {
  164.       if (*pat == '*') pat++;    /* yes, skip leading * (old Pine behavior) */
  165.       strcpy (tmp,pat);        /* copy host name */
  166.       bbd = tmp + (patx - pat);    /* where we write the bboards */
  167.     }
  168.     else {            /* no host specification */
  169.       bbd = tmp;        /* no prefix */
  170.       patx = pat;        /* use entire specification */
  171.     }
  172.                 /* ask server for all active newsgroups */
  173.     if (!(nntp_send (LOCAL->nntpstream,"LIST","ACTIVE") == NNTPGLIST)) return;
  174.                 /* process data until we see final dot */
  175.     while ((s = tcp_getline (LOCAL->nntpstream->tcpstream)) && *s != '.') {
  176.                 /* tie off after newsgroup name */
  177.       if (t = strchr (s,' ')) *t = '\0';
  178.       if (pmatch (s,patx)) {    /* report to main program if have match */
  179.     strcpy (bbd,s);        /* write newsgroup name after prefix */
  180.     mm_bboard (tmp);
  181.       }
  182.       fs_give ((void **) &s);    /* clean up */
  183.     }
  184.   }
  185. }
  186.  
  187. /* NNTP mail subscribe to mailbox
  188.  * Accepts: mail stream
  189.  *        mailbox to add to subscription list
  190.  * Returns: T on success, NIL on failure
  191.  */
  192.  
  193. long nntp_subscribe (MAILSTREAM *stream,char *mailbox)
  194. {
  195.   return NIL;            /* never valid for NNTP */
  196. }
  197.  
  198.  
  199. /* NNTP mail unsubscribe to mailbox
  200.  * Accepts: mail stream
  201.  *        mailbox to delete from subscription list
  202.  * Returns: T on success, NIL on failure
  203.  */
  204.  
  205. long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox)
  206. {
  207.   return NIL;            /* never valid for NNTP */
  208. }
  209.  
  210.  
  211. /* NNTP mail subscribe to bboard
  212.  * Accepts: mail stream
  213.  *        bboard to add to subscription list
  214.  * Returns: T on success, NIL on failure
  215.  */
  216.  
  217. long nntp_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  218. {
  219.   char *s = strchr (mailbox,'}');
  220.   return s ? newsrc_update (s+1,':') : NIL;
  221. }
  222.  
  223.  
  224. /* NNTP mail unsubscribe to bboard
  225.  * Accepts: mail stream
  226.  *        bboard to delete from subscription list
  227.  * Returns: T on success, NIL on failure
  228.  */
  229.  
  230. long nntp_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  231. {
  232.   char *s = strchr (mailbox,'}');
  233.   return s ? newsrc_update (s+1,'!') : NIL;
  234. }
  235.  
  236. /* NNTP mail create mailbox
  237.  * Accepts: mail stream
  238.  *        mailbox name to create
  239.  * Returns: T on success, NIL on failure
  240.  */
  241.  
  242. long nntp_create (MAILSTREAM *stream,char *mailbox)
  243. {
  244.   return NIL;            /* never valid for NNTP */
  245. }
  246.  
  247.  
  248. /* NNTP mail delete mailbox
  249.  *        mailbox name to delete
  250.  * Returns: T on success, NIL on failure
  251.  */
  252.  
  253. long nntp_delete (MAILSTREAM *stream,char *mailbox)
  254. {
  255.   return NIL;            /* never valid for NNTP */
  256. }
  257.  
  258.  
  259. /* NNTP mail rename mailbox
  260.  * Accepts: mail stream
  261.  *        old mailbox name
  262.  *        new mailbox name
  263.  * Returns: T on success, NIL on failure
  264.  */
  265.  
  266. long nntp_rename (MAILSTREAM *stream,char *old,char *new)
  267. {
  268.   return NIL;            /* never valid for NNTP */
  269. }
  270.  
  271. /* NNTP mail open
  272.  * Accepts: stream to open
  273.  * Returns: stream on success, NIL on failure
  274.  */
  275.  
  276. MAILSTREAM *nntp_mopen (MAILSTREAM *stream)
  277. {
  278.   long i,j,k;
  279.   long nmsgs = 0;
  280.   long unseen = 0;
  281.   char c = NIL,*s,*t,tmp[MAILTMPLEN];
  282.   NETMBX mb;
  283.   void *tcpstream;
  284.   SMTPSTREAM *nstream = NIL;
  285.                 /* return prototype for OP_PROTOTYPE call */
  286.   if (!stream) return &nntpproto;
  287.   mail_valid_net_parse (stream->mailbox,&mb);
  288.   if (!*mb.mailbox) strcpy (mb.mailbox,"general");
  289.   if (LOCAL) {            /* if recycle stream, see if changing hosts */
  290.     if (strcmp (lcase (mb.host),lcase (strcpy (tmp,LOCAL->host)))) {
  291.       sprintf (tmp,"Closing connection to %s",LOCAL->host);
  292.       if (!stream->silent) mm_log (tmp,(long) NIL);
  293.     }
  294.     else {            /* same host, preserve NNTP connection */
  295.       sprintf (tmp,"Reusing connection to %s",LOCAL->host);
  296.       if (!stream->silent) mm_log (tmp,(long) NIL);
  297.       nstream = LOCAL->nntpstream;
  298.       LOCAL->nntpstream = NIL;    /* keep nntp_close() from punting it */
  299.     }
  300.     nntp_close (stream);    /* do close action */
  301.     stream->dtb = &nntpdriver;/* reattach this driver */
  302.     mail_free_cache (stream);    /* clean up cache */
  303.   }
  304.                 /* in case /debug switch given */
  305.   if (mb.dbgflag) stream->debug = T;
  306.  
  307.   if (!nstream) {        /* open NNTP now if not already open */
  308.     char *hostlist[2];
  309.     hostlist[0] = strcpy (tmp,mb.host);
  310.     if (mb.port) sprintf (tmp + strlen (tmp),":%ld",mb.port);
  311.     hostlist[1] = NIL;
  312.     nstream = nntp_open (hostlist,OP_READONLY+(stream->debug ? OP_DEBUG:NIL));
  313.   }
  314.   if (nstream) {        /* now try to open newsgroup */
  315.     if ((!stream->halfopen) &&    /* open the newsgroup if not halfopen */
  316.     ((nntp_send (nstream,"GROUP",mb.mailbox) != NNTPGOK) ||
  317.      ((k = strtol (nstream->reply + 4,&s,10)) < 0) ||
  318.      ((i = strtol (s,&s,10)) < 0) || ((j = strtol (s,&s,10)) < 0) ||
  319.      (nmsgs = i | j ? 1 + j - i : 0) < 0)) {
  320.       mm_log (nstream->reply,ERROR);
  321.       smtp_close (nstream);    /* punt stream */
  322.       nstream = NIL;
  323.       return NIL;
  324.     }
  325.                 /* newsgroup open, instantiate local data */
  326.     stream->local = fs_get (sizeof (NNTPLOCAL));
  327.     LOCAL->nntpstream = nstream;
  328.     LOCAL->dirty = NIL;        /* no update to .newsrc needed yet */
  329.                 /* copy host and newsgroup name */
  330.     LOCAL->host = cpystr (mb.host);
  331.     LOCAL->name = cpystr (mb.mailbox);
  332.     stream->sequence++;        /* bump sequence number */
  333.     stream->rdonly = T;        /* make sure higher level knows readonly */
  334.     LOCAL->number = NIL;
  335.     LOCAL->header = LOCAL->body = NIL;
  336.     LOCAL->buf = NIL;
  337.  
  338.     if (!stream->halfopen) {    /* if not half-open */
  339.       if (nmsgs) {        /* find what messages exist */
  340.     LOCAL->number = (unsigned long *) fs_get (nmsgs*sizeof(unsigned long));
  341.     sprintf (tmp,"%ld-%ld",i,j);
  342.     if ((nntp_send (nstream,"LISTGROUP",mb.mailbox) == NNTPGOK) ||
  343.         (nntp_send (nstream,"XHDR Date",tmp) == NNTPHEAD)) {
  344.       for (i = 0; (s = tcp_getline (nstream->tcpstream)) && strcmp (s,".");
  345.            i++) {        /* initialize c-client/NNTP map */
  346.         if (i < nmsgs) LOCAL->number[i] = atol (s);
  347.         fs_give ((void **) &s);
  348.       }
  349.       if (s) fs_give ((void **) &s);
  350.       if (i != nmsgs) {    /* found holes? */
  351.         if (nmsgs = i)
  352.           fs_resize ((void **) &LOCAL->number,nmsgs*sizeof(unsigned long));
  353.         else fs_give ((void **) &LOCAL->number);
  354.       }
  355.     }
  356.                 /* assume c-client/NNTP map is entire range */
  357.     else for (k = 0; k < nmsgs; ++k) LOCAL->number[k] = i + k;
  358.                 /* create caches */
  359.     LOCAL->header = (char **) fs_get (nmsgs * sizeof (char *));
  360.     LOCAL->body = (char **) fs_get (nmsgs * sizeof (char *));
  361.                 /* initialize per-message cache */
  362.     for (i = 0; i < nmsgs; ++i) LOCAL->header[i] = LOCAL->body[i] = NIL;
  363.       }
  364.                 /* make temporary buffer */
  365.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1);
  366.                 /* notify upper level that messages exist */
  367.       mail_exists (stream,nmsgs);
  368.                 /* read .newsrc entries */
  369.       mail_recent (stream,newsrc_read (LOCAL->name,stream,LOCAL->number));
  370.                 /* notify if empty bboard */
  371.       if (!(stream->nmsgs || stream->silent)) {
  372.     sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
  373.     mm_log (tmp,WARN);
  374.       }
  375.     }
  376.   }
  377.   return LOCAL ? stream : NIL;    /* if stream is alive, return to caller */
  378. }
  379.  
  380. /* NNTP mail close
  381.  * Accepts: MAIL stream
  382.  */
  383.  
  384. void nntp_close (MAILSTREAM *stream)
  385. {
  386.   if (LOCAL) {            /* only if a file is open */
  387.     nntp_check (stream);    /* dump final checkpoint */
  388.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  389.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  390.     nntp_gc (stream,GC_TEXTS);    /* free local cache */
  391.     if (LOCAL->number) fs_give ((void **) &LOCAL->number);
  392.     if (LOCAL->header) fs_give ((void **) &LOCAL->header);
  393.     if (LOCAL->body) fs_give ((void **) &LOCAL->body);
  394.                 /* free local scratch buffer */
  395.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  396.                 /* close NNTP connection */
  397.     if (LOCAL->nntpstream) smtp_close (LOCAL->nntpstream);
  398.                 /* nuke the local data */
  399.     fs_give ((void **) &stream->local);
  400.     stream->dtb = NIL;        /* log out the DTB */
  401.   }
  402. }
  403.  
  404. /* NNTP mail fetch fast information
  405.  * Accepts: MAIL stream
  406.  *        sequence
  407.  */
  408.  
  409. void nntp_fetchfast (MAILSTREAM *stream,char *sequence)
  410. {
  411.   long i;
  412.   BODY *b;
  413.                 /* ugly and slow */
  414.   if (stream && LOCAL && mail_sequence (stream,sequence))
  415.     for (i = 1; i <= stream->nmsgs; i++)
  416.       if (mail_elt (stream,i)->sequence)
  417.     nntp_fetchstructure (stream,i,&b);
  418. }
  419.  
  420.  
  421. /* NNTP mail fetch flags
  422.  * Accepts: MAIL stream
  423.  *        sequence
  424.  */
  425.  
  426. void nntp_fetchflags (MAILSTREAM *stream,char *sequence)
  427. {
  428.   return;            /* no-op for local mail */
  429. }
  430.  
  431. /* NNTP mail fetch envelope
  432.  * Accepts: MAIL stream
  433.  *        message # to fetch
  434.  *        pointer to return body
  435.  * Returns: envelope of this message, body returned in body value
  436.  *
  437.  * Fetches the "fast" information as well
  438.  */
  439.  
  440. ENVELOPE *nntp_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  441. {
  442.   char *h,*t;
  443.   LONGCACHE *lelt;
  444.   ENVELOPE **env;
  445.   STRING bs;
  446.   BODY **b;
  447.   unsigned long hdrsize;
  448.   unsigned long textsize = 0;
  449.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  450.   if (stream->scache) {        /* short cache */
  451.     if (msgno != stream->msgno){/* flush old poop if a different message */
  452.       mail_free_envelope (&stream->env);
  453.       mail_free_body (&stream->body);
  454.     }
  455.     stream->msgno = msgno;
  456.     env = &stream->env;        /* get pointers to envelope and body */
  457.     b = &stream->body;
  458.   }
  459.   else {            /* long cache */
  460.     lelt = mail_lelt (stream,msgno);
  461.     env = &lelt->env;        /* get pointers to envelope and body */
  462.     b = &lelt->body;
  463.   }
  464.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  465.     mail_free_envelope (env);    /* flush old envelope and body */
  466.     mail_free_body (b);
  467.     hdrsize = strlen (h = nntp_fetchheader (stream,msgno));
  468.     if (body) {            /* only if want to parse body */
  469.       textsize = strlen (t = nntp_fetchtext_work (stream,msgno));
  470.                 /* calculate message size */
  471.       elt->rfc822_size = hdrsize + textsize;
  472.       INIT (&bs,mail_string,(void *) t,textsize);
  473.     }
  474.                 /* parse envelope and body */
  475.     rfc822_parse_msg (env,body ? b : NIL,h,hdrsize,body ? &bs : NIL,BADHOST,
  476.               LOCAL->buf);
  477.                 /* parse date */
  478.     if (*env && (*env)->date) mail_parse_date (elt,(*env)->date);
  479.     if (!elt->month) mail_parse_date (elt,"01-JAN-1969 00:00:00 GMT");
  480.   }
  481.   if (body) *body = *b;        /* return the body */
  482.   return *env;            /* return the envelope */
  483. }
  484.  
  485. /* NNTP mail fetch message header
  486.  * Accepts: MAIL stream
  487.  *        message # to fetch
  488.  * Returns: message header in RFC822 format
  489.  */
  490.  
  491. char *nntp_fetchheader (MAILSTREAM *stream,long msgno)
  492. {
  493.   char tmp[MAILTMPLEN];
  494.   long m = msgno - 1;
  495.   if (!LOCAL->header[m]) {    /* fetch header if don't have already */
  496.     sprintf (tmp,"%ld",LOCAL->number[m]);
  497.     if (nntp_send (LOCAL->nntpstream,"HEAD",tmp) == NNTPHEAD)
  498.       LOCAL->header[m] = nntp_slurp (stream);
  499.                 /* failed, mark as deleted */
  500.     else mail_elt (stream,msgno)->deleted = T;
  501.   }
  502.   return LOCAL->header[m] ? LOCAL->header[m] : "";
  503. }
  504.  
  505.  
  506. /* NNTP mail fetch message text (body only)
  507.  * Accepts: MAIL stream
  508.  *        message # to fetch
  509.  * Returns: message text in RFC822 format
  510.  */
  511.  
  512. char *nntp_fetchtext (MAILSTREAM *stream,long msgno)
  513. {
  514.   long m = msgno - 1;
  515.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  516.   elt->seen = T;        /* mark as seen */
  517.   return nntp_fetchtext_work (stream,msgno);
  518. }
  519.  
  520.  
  521. /* NNTP mail fetch message text work
  522.  * Accepts: MAIL stream
  523.  *        message # to fetch
  524.  * Returns: message text in RFC822 format
  525.  */
  526.  
  527. char *nntp_fetchtext_work (MAILSTREAM *stream,long msgno)
  528. {
  529.   char tmp[MAILTMPLEN];
  530.   long m = msgno - 1;
  531.   if (!LOCAL->body[m]) {    /* fetch body if don't have already */
  532.     sprintf (tmp,"%ld",LOCAL->number[m]);
  533.     if (nntp_send (LOCAL->nntpstream,"BODY",tmp) == NNTPBODY)
  534.       LOCAL->body[m] = nntp_slurp (stream);
  535.                 /* failed, mark as deleted */
  536.     else mail_elt (stream,msgno)->deleted = T;
  537.   }
  538.   return LOCAL->body[m] ? LOCAL->body[m] : "";
  539. }
  540.  
  541. /* NNTP mail slurp NNTP dot-terminated text
  542.  * Accepts: MAIL stream
  543.  * Returns: text
  544.  */
  545.  
  546. char *nntp_slurp (MAILSTREAM *stream)
  547. {
  548.   char *s,*t;
  549.   unsigned long i;
  550.   unsigned long bufpos = 0;
  551.   while (s = tcp_getline (LOCAL->nntpstream->tcpstream)) {
  552.     if (*s == '.') {        /* possible end of text? */
  553.       if (s[1]) t = s + 1;    /* pointer to true start of line */
  554.       else {
  555.     fs_give ((void **) &s);    /* free the line */
  556.     break;            /* end of data */
  557.       }
  558.     }
  559.     else t = s;            /* want the entire line */
  560.                 /* ensure have enough room */
  561.     if (LOCAL->buflen < (bufpos + (i = strlen (t)) + 5))
  562.       fs_resize ((void **) &LOCAL->buf,LOCAL->buflen += (MAXMESSAGESIZE + 1));
  563.                 /* copy the text */
  564.     strncpy (LOCAL->buf + bufpos,t,i);
  565.     bufpos += i;        /* set new buffer position */
  566.     LOCAL->buf[bufpos++] = '\015';
  567.     LOCAL->buf[bufpos++] = '\012';
  568.     fs_give ((void **) &s);    /* free the line */
  569.   }
  570.   LOCAL->buf[bufpos++] = '\015';/* add final newline */
  571.   LOCAL->buf[bufpos++] = '\012';
  572.   LOCAL->buf[bufpos++] = '\0';    /* tie off string with NUL */
  573.   return cpystr (LOCAL->buf);    /* return copy of collected string */
  574. }
  575.  
  576. /* NNTP fetch message body as a structure
  577.  * Accepts: Mail stream
  578.  *        message # to fetch
  579.  *        section specifier
  580.  *        pointer to length
  581.  * Returns: pointer to section of message body
  582.  */
  583.  
  584. char *nntp_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  585. {
  586.   BODY *b;
  587.   PART *pt;
  588.   unsigned long i;
  589.   char *base;
  590.   unsigned long offset = 0;
  591.   MESSAGECACHE *elt = mail_elt (stream,m);
  592.                 /* make sure have a body */
  593.   if (!(nntp_fetchstructure (stream,m,&b) && b && s && *s &&
  594.     ((i = strtol (s,&s,10)) > 0) &&
  595.     (base = nntp_fetchtext_work (stream,m))))
  596.     return NIL;
  597.   do {                /* until find desired body part */
  598.                 /* multipart content? */
  599.     if (b->type == TYPEMULTIPART) {
  600.       pt = b->contents.part;    /* yes, find desired part */
  601.       while (--i && (pt = pt->next));
  602.       if (!pt) return NIL;    /* bad specifier */
  603.                 /* note new body, check valid nesting */
  604.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  605.       offset = pt->offset;    /* get new offset */
  606.     }
  607.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  608.                 /* need to go down further? */
  609.     if (i = *s) switch (b->type) {
  610.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  611.       offset = b->contents.msg.offset;
  612.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  613.     case TYPEMULTIPART:        /* multipart, get next section */
  614.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  615.     default:            /* bogus subpart specification */
  616.       return NIL;
  617.     }
  618.   } while (i);
  619.                 /* lose if body bogus */
  620.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  621.   elt->seen = T;        /* mark as seen */
  622.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  623.               b->size.ibytes,b->encoding);
  624. }
  625.  
  626. /* NNTP mail set flag
  627.  * Accepts: MAIL stream
  628.  *        sequence
  629.  *        flag(s)
  630.  */
  631.  
  632. void nntp_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  633. {
  634.   MESSAGECACHE *elt;
  635.   long i;
  636.   short f = nntp_getflags (stream,flag);
  637.   if (!f) return;        /* no-op if no flags to modify */
  638.                 /* get sequence and loop on it */
  639.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  640.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  641.       if (f&fSEEN) elt->seen=T;    /* set all requested flags */
  642.       if (f&fDELETED) {        /* deletion also purges the cache */
  643.     elt->deleted = T;    /* mark deleted */
  644.     LOCAL->dirty = T;    /* mark dirty */
  645.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  646.     if (LOCAL->body[i]) fs_give ((void **) &LOCAL->body[i]);
  647.       }
  648.       if (f&fFLAGGED) elt->flagged = T;
  649.       if (f&fANSWERED) elt->answered = T;
  650.     }
  651. }
  652.  
  653.  
  654. /* NNTP mail clear flag
  655.  * Accepts: MAIL stream
  656.  *        sequence
  657.  *        flag(s)
  658.  */
  659.  
  660. void nntp_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  661. {
  662.   MESSAGECACHE *elt;
  663.   long i;
  664.   short f = nntp_getflags (stream,flag);
  665.   if (!f) return;        /* no-op if no flags to modify */
  666.                 /* get sequence and loop on it */
  667.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  668.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  669.                 /* clear all requested flags */
  670.       if (f&fSEEN) elt->seen = NIL;
  671.       if (f&fDELETED) {
  672.     elt->deleted = NIL;    /* undelete */
  673.     LOCAL->dirty = T;    /* mark stream as dirty */
  674.       }
  675.       if (f&fFLAGGED) elt->flagged = NIL;
  676.       if (f&fANSWERED) elt->answered = NIL;
  677.     }
  678. }
  679.  
  680. /* NNTP mail search for messages
  681.  * Accepts: MAIL stream
  682.  *        search criteria
  683.  */
  684.  
  685. void nntp_search (MAILSTREAM *stream,char *criteria)
  686. {
  687.   long i,n;
  688.   char *d;
  689.   search_t f;
  690.                 /* initially all searched */
  691.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  692.                 /* get first criterion */
  693.   if (criteria && (criteria = strtok (criteria," "))) {
  694.                 /* for each criterion */
  695.     for (; criteria; (criteria = strtok (NIL," "))) {
  696.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  697.       switch (*ucase (criteria)) {
  698.       case 'A':            /* possible ALL, ANSWERED */
  699.     if (!strcmp (criteria+1,"LL")) f = nntp_search_all;
  700.     else if (!strcmp (criteria+1,"NSWERED")) f = nntp_search_answered;
  701.     break;
  702.       case 'B':            /* possible BCC, BEFORE, BODY */
  703.     if (!strcmp (criteria+1,"CC"))
  704.       f = nntp_search_string (nntp_search_bcc,&d,&n);
  705.     else if (!strcmp (criteria+1,"EFORE"))
  706.       f = nntp_search_date (nntp_search_before,&n);
  707.     else if (!strcmp (criteria+1,"ODY"))
  708.       f = nntp_search_string (nntp_search_body,&d,&n);
  709.     break;
  710.       case 'C':            /* possible CC */
  711.     if (!strcmp (criteria+1,"C")) 
  712.       f = nntp_search_string (nntp_search_cc,&d,&n);
  713.     break;
  714.       case 'D':            /* possible DELETED */
  715.     if (!strcmp (criteria+1,"ELETED")) f = nntp_search_deleted;
  716.     break;
  717.       case 'F':            /* possible FLAGGED, FROM */
  718.     if (!strcmp (criteria+1,"LAGGED")) f = nntp_search_flagged;
  719.     else if (!strcmp (criteria+1,"ROM"))
  720.       f = nntp_search_string (nntp_search_from,&d,&n);
  721.     break;
  722.       case 'K':            /* possible KEYWORD */
  723.     if (!strcmp (criteria+1,"EYWORD"))
  724.       f = nntp_search_flag (nntp_search_keyword,&d);
  725.     break;
  726.       case 'N':            /* possible NEW */
  727.     if (!strcmp (criteria+1,"EW")) f = nntp_search_new;
  728.     break;
  729.  
  730.       case 'O':            /* possible OLD, ON */
  731.     if (!strcmp (criteria+1,"LD")) f = nntp_search_old;
  732.     else if (!strcmp (criteria+1,"N"))
  733.       f = nntp_search_date (nntp_search_on,&n);
  734.     break;
  735.       case 'R':            /* possible RECENT */
  736.     if (!strcmp (criteria+1,"ECENT")) f = nntp_search_recent;
  737.     break;
  738.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  739.     if (!strcmp (criteria+1,"EEN")) f = nntp_search_seen;
  740.     else if (!strcmp (criteria+1,"INCE"))
  741.       f = nntp_search_date (nntp_search_since,&n);
  742.     else if (!strcmp (criteria+1,"UBJECT"))
  743.       f = nntp_search_string (nntp_search_subject,&d,&n);
  744.     break;
  745.       case 'T':            /* possible TEXT, TO */
  746.     if (!strcmp (criteria+1,"EXT"))
  747.       f = nntp_search_string (nntp_search_text,&d,&n);
  748.     else if (!strcmp (criteria+1,"O"))
  749.       f = nntp_search_string (nntp_search_to,&d,&n);
  750.     break;
  751.       case 'U':            /* possible UN* */
  752.     if (criteria[1] == 'N') {
  753.       if (!strcmp (criteria+2,"ANSWERED")) f = nntp_search_unanswered;
  754.       else if (!strcmp (criteria+2,"DELETED")) f = nntp_search_undeleted;
  755.       else if (!strcmp (criteria+2,"FLAGGED")) f = nntp_search_unflagged;
  756.       else if (!strcmp (criteria+2,"KEYWORD"))
  757.         f = nntp_search_flag (nntp_search_unkeyword,&d);
  758.       else if (!strcmp (criteria+2,"SEEN")) f = nntp_search_unseen;
  759.     }
  760.     break;
  761.       default:            /* we will barf below */
  762.     break;
  763.       }
  764.       if (!f) {            /* if can't determine any criteria */
  765.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  766.     mm_log (LOCAL->buf,ERROR);
  767.     return;
  768.       }
  769.                 /* run the search criterion */
  770.       for (i = 1; i <= stream->nmsgs; ++i)
  771.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  772.       mail_elt (stream,i)->searched = NIL;
  773.     }
  774.                 /* report search results to main program */
  775.     for (i = 1; i <= stream->nmsgs; ++i)
  776.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  777.   }
  778. }
  779.  
  780. /* NNTP mail ping mailbox
  781.  * Accepts: MAIL stream
  782.  * Returns: T if stream alive, else NIL
  783.  */
  784.  
  785. long nntp_ping (MAILSTREAM *stream)
  786. {
  787.   /* Kludge alert: SMTPSOFTFATAL is 421 which is used in NNTP to mean ``No
  788.    * next article in this group''.  Hopefully, no NNTP server will send this
  789.    * in response to a STAT */
  790.   return (nntp_send (LOCAL->nntpstream,"STAT",NIL) != SMTPSOFTFATAL);
  791. }
  792.  
  793.  
  794. /* NNTP mail check mailbox
  795.  * Accepts: MAIL stream
  796.  */
  797.  
  798. void nntp_check (MAILSTREAM *stream)
  799. {
  800.                 /* never do if no updates */
  801.   if (LOCAL->dirty) newsrc_write (LOCAL->name,stream,LOCAL->number);
  802.   LOCAL->dirty = NIL;
  803. }
  804.  
  805. /* NNTP mail expunge mailbox
  806.  * Accepts: MAIL stream
  807.  */
  808.  
  809. void nntp_expunge (MAILSTREAM *stream)
  810. {
  811.   if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
  812. }
  813.  
  814.  
  815. /* NNTP mail copy message(s)
  816.  * Accepts: MAIL stream
  817.  *        sequence
  818.  *        destination mailbox
  819.  * Returns: T if copy successful, else NIL
  820.  */
  821.  
  822. long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  823. {
  824.   mm_log ("Copy not valid for NNTP",ERROR);
  825.   return NIL;
  826. }
  827.  
  828.  
  829. /* NNTP mail move message(s)
  830.  * Accepts: MAIL stream
  831.  *        sequence
  832.  *        destination mailbox
  833.  * Returns: T if move successful, else NIL
  834.  */
  835.  
  836. long nntp_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  837. {
  838.   mm_log ("Move not valid for NNTP",ERROR);
  839.   return NIL;
  840. }
  841.  
  842.  
  843. /* NNTP mail append message from stringstruct
  844.  * Accepts: MAIL stream
  845.  *        destination mailbox
  846.  *        stringstruct of messages to append
  847.  * Returns: T if append successful, else NIL
  848.  */
  849.  
  850. long nntp_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  851.           STRING *message)
  852. {
  853.   mm_log ("Append not valid for NNTP",ERROR);
  854.   return NIL;
  855. }
  856.  
  857. /* NNTP garbage collect stream
  858.  * Accepts: Mail stream
  859.  *        garbage collection flags
  860.  */
  861.  
  862. void nntp_gc (MAILSTREAM *stream,long gcflags)
  863. {
  864.   unsigned long i;
  865.   if (!stream->halfopen)     /* never on half-open stream */
  866.     if (gcflags & GC_TEXTS)    /* garbage collect texts? */
  867.                 /* flush texts from cache */
  868.       for (i = 0; i < stream->nmsgs; i++) {
  869.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  870.     if (LOCAL->body[i]) fs_give ((void **) &LOCAL->body[i]);
  871.       }
  872. }
  873.  
  874. /* Internal routines */
  875.  
  876.  
  877. /* Parse flag list
  878.  * Accepts: MAIL stream
  879.  *        flag list as a character string
  880.  * Returns: flag command list
  881.  */
  882.  
  883. short nntp_getflags (MAILSTREAM *stream,char *flag)
  884. {
  885.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  886.   short f = 0;
  887.   short i,j;
  888.   if (flag && *flag) {        /* no-op if no flag string */
  889.                 /* check if a list and make sure valid */
  890.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  891.       mm_log ("Bad flag list",ERROR);
  892.       return NIL;
  893.     }
  894.                 /* copy the flag string w/o list construct */
  895.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  896.     tmp[j] = '\0';
  897.     t = ucase (tmp);        /* uppercase only from now on */
  898.  
  899.     while (t && *t) {        /* parse the flags */
  900.       if (*t == '\\') {        /* system flag? */
  901.     switch (*++t) {        /* dispatch based on first character */
  902.     case 'S':        /* possible \Seen flag */
  903.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  904.       t += 4;        /* skip past flag name */
  905.       break;
  906.     case 'D':        /* possible \Deleted flag */
  907.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  908.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  909.       t += 7;        /* skip past flag name */
  910.       break;
  911.     case 'F':        /* possible \Flagged flag */
  912.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  913.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  914.       t += 7;        /* skip past flag name */
  915.       break;
  916.     case 'A':        /* possible \Answered flag */
  917.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  918.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  919.       t += 8;        /* skip past flag name */
  920.       break;
  921.     default:        /* unknown */
  922.       i = 0;
  923.       break;
  924.     }
  925.                 /* add flag to flags list */
  926.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  927.       }
  928.       else {            /* no user flags yet */
  929.     t = strtok (t," ");    /* isolate flag name */
  930.     sprintf (err,"Unknown flag: %.80s",t);
  931.     t = strtok (NIL," ");    /* get next flag */
  932.     mm_log (err,ERROR);
  933.       }
  934.     }
  935.   }
  936.   return f;
  937. }
  938.  
  939. /* Search support routines
  940.  * Accepts: MAIL stream
  941.  *        message number
  942.  *        pointer to additional data
  943.  *        pointer to temporary buffer
  944.  * Returns: T if search matches, else NIL
  945.  */
  946.  
  947. char nntp_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  948. {
  949.   return T;            /* ALL always succeeds */
  950. }
  951.  
  952.  
  953. char nntp_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  954. {
  955.   return mail_elt (stream,msgno)->answered ? T : NIL;
  956. }
  957.  
  958.  
  959. char nntp_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  960. {
  961.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  962. }
  963.  
  964.  
  965. char nntp_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  966. {
  967.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  968. }
  969.  
  970.  
  971. char nntp_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  972. {
  973.   return NIL;            /* keywords not supported yet */
  974. }
  975.  
  976.  
  977. char nntp_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  978. {
  979.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  980.   return (elt->recent && !elt->seen) ? T : NIL;
  981. }
  982.  
  983. char nntp_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  984. {
  985.   return mail_elt (stream,msgno)->recent ? NIL : T;
  986. }
  987.  
  988.  
  989. char nntp_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  990. {
  991.   return mail_elt (stream,msgno)->recent ? T : NIL;
  992. }
  993.  
  994.  
  995. char nntp_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  996. {
  997.   return mail_elt (stream,msgno)->seen ? T : NIL;
  998. }
  999.  
  1000.  
  1001. char nntp_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1002. {
  1003.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1004. }
  1005.  
  1006.  
  1007. char nntp_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1008. {
  1009.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1010. }
  1011.  
  1012.  
  1013. char nntp_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1014. {
  1015.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1016. }
  1017.  
  1018.  
  1019. char nntp_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1020. {
  1021.   return T;            /* keywords not supported yet */
  1022. }
  1023.  
  1024.  
  1025. char nntp_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1026. {
  1027.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1028. }
  1029.  
  1030. char nntp_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1031. {
  1032.   return (char) (nntp_msgdate (stream,msgno) < n);
  1033. }
  1034.  
  1035.  
  1036. char nntp_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1037. {
  1038.   return (char) (nntp_msgdate (stream,msgno) == n);
  1039. }
  1040.  
  1041.  
  1042. char nntp_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1043. {
  1044.                 /* everybody interprets "since" as .GE. */
  1045.   return (char) (nntp_msgdate (stream,msgno) >= n);
  1046. }
  1047.  
  1048.  
  1049. unsigned long nntp_msgdate (MAILSTREAM *stream,long msgno)
  1050. {
  1051.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1052.                 /* get date if don't have it yet */
  1053.   if (!elt->day) nntp_fetchstructure (stream,msgno,NIL);
  1054.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  1055. }
  1056.  
  1057.  
  1058. char nntp_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1059. {
  1060.   char *t = nntp_fetchtext_work (stream,msgno);
  1061.   return (t && search (t,(unsigned long) strlen (t),d,n));
  1062. }
  1063.  
  1064.  
  1065. char nntp_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1066. {
  1067.   char *t = nntp_fetchstructure (stream,msgno,NIL)->subject;
  1068.   return t ? search (t,strlen (t),d,n) : NIL;
  1069. }
  1070.  
  1071.  
  1072. char nntp_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1073. {
  1074.   char *t = nntp_fetchheader (stream,msgno);
  1075.   return (t && search (t,strlen (t),d,n)) ||
  1076.     nntp_search_body (stream,msgno,d,n);
  1077. }
  1078.  
  1079. char nntp_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1080. {
  1081.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->bcc;
  1082.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1083.                 /* get text for address */
  1084.   rfc822_write_address (LOCAL->buf,a);
  1085.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1086. }
  1087.  
  1088.  
  1089. char nntp_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1090. {
  1091.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->cc;
  1092.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1093.                 /* get text for address */
  1094.   rfc822_write_address (LOCAL->buf,a);
  1095.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1096. }
  1097.  
  1098.  
  1099. char nntp_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1100. {
  1101.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->from;
  1102.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1103.                 /* get text for address */
  1104.   rfc822_write_address (LOCAL->buf,a);
  1105.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1106. }
  1107.  
  1108.  
  1109. char nntp_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1110. {
  1111.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->to;
  1112.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1113.                 /* get text for address */
  1114.   rfc822_write_address (LOCAL->buf,a);
  1115.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1116. }
  1117.  
  1118. /* Search parsers */
  1119.  
  1120.  
  1121. /* Parse a date
  1122.  * Accepts: function to return
  1123.  *        pointer to date integer to return
  1124.  * Returns: function to return
  1125.  */
  1126.  
  1127. search_t nntp_search_date (search_t f,long *n)
  1128. {
  1129.   long i;
  1130.   char *s;
  1131.   MESSAGECACHE elt;
  1132.                 /* parse the date and return fn if OK */
  1133.   return (nntp_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1134.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1135. }
  1136.  
  1137. /* Parse a flag
  1138.  * Accepts: function to return
  1139.  *        pointer to string to return
  1140.  * Returns: function to return
  1141.  */
  1142.  
  1143. search_t nntp_search_flag (search_t f,char **d)
  1144. {
  1145.                 /* get a keyword, return if OK */
  1146.   return (*d = strtok (NIL," ")) ? f : NIL;
  1147. }
  1148.  
  1149.  
  1150. /* Parse a string
  1151.  * Accepts: function to return
  1152.  *        pointer to string to return
  1153.  *        pointer to string length to return
  1154.  * Returns: function to return
  1155.  */
  1156.  
  1157. search_t nntp_search_string (search_t f,char **d,long *n)
  1158. {
  1159.   char *end = " ";
  1160.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1161.   if (!c) return NIL;        /* missing argument */
  1162.   switch (*c) {            /* see what the argument is */
  1163.   case '{':            /* literal string */
  1164.     *n = strtol (c+1,d,10);    /* get its length */
  1165.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1166.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1167.       char e = *--c;
  1168.       *c = DELIM;        /* make sure not a space */
  1169.       strtok (c," ");        /* reset the strtok mechanism */
  1170.       *c = e;            /* put character back */
  1171.       break;
  1172.     }
  1173.   case '\0':            /* catch bogons */
  1174.   case ' ':
  1175.     return NIL;
  1176.   case '"':            /* quoted string */
  1177.     if (strchr (c+1,'"')) end = "\"";
  1178.     else return NIL;
  1179.   default:            /* atomic string */
  1180.     if (*d = strtok (c,end)) *n = strlen (*d);
  1181.     else return NIL;
  1182.     break;
  1183.   }
  1184.   return f;
  1185. }
  1186.